Multi-category dot-density maps often work well when the categories cluster geographically. Recent immigrants by country of origin work well for this. ## Data First we grab the immigrant data via cancensus, making use of the CensusMapper API tool to select the regions and variables we need.

#devtools::install_github("mountainmath/cancensus")
library(cancensus)
library(dotdensity)
# options(cancensus.api_key='your_api_key')
regions=list(CMA="59933")
vectors_2011=c("v_CA11N_265","v_CA11N_268","v_CA11N_304","v_CA11N_334","v_CA11N_373","v_CA11N_376","v_CA11N_379","v_CA11N_382")
vectors=c("v_CA16_3636","v_CA16_3639","v_CA16_3669","v_CA16_3699","v_CA16_3741","v_CA16_3810","v_CA16_3750","v_CA16_3756","v_CA16_3783")

We choose the categories and colours we want to map and define a convenience function to rename the variables and compute the qantities for the other asian countries that we don’t break out.

categories=c("Americas","Europe","Africa + Oceania","Philippines","China","India","Other Asian Countries")
colors=c("#7a0177", "#3333cc", "#ff00ff", "#00ffff", "#ff1a1c", "#4dff4a", "#ffff33")
prep_data <- function(geo){
  data <- geo %>% replace(is.na(.), 0)
  data <- rename(data,
    total=v_CA16_3636,
    Americas=v_CA16_3639,                                  
    Europe=v_CA16_3669,
    Philippines=v_CA16_3783,
    China=v_CA16_3750,
    India=v_CA16_3756)
  data %>% mutate(`Other Asian Countries` = v_CA16_3741-Philippines-China-India,
                     `Africa + Oceania`=v_CA16_3699 + v_CA16_3810)
}
prep_data_2011 <- function(geo){
  data <- geo %>% replace(is.na(.), 0)
  data <- rename(data,
    total=v_CA11N_265,
    Americas=v_CA11N_268,                                  
    Europe=v_CA11N_304,
    Africa=v_CA11N_334,
    Philippines=v_CA11N_376,
    China=v_CA11N_379,
    India=v_CA11N_382)
  data %>% mutate(`Other Asian Countries` = v_CA11N_373-Philippines-China-India)
}

Next we grab the data via cancensus,

data_csd=get_census(dataset = 'CA16', regions=regions,vectors=vectors,geo_format='sp',labels='short',level='CSD') %>% prep_data
OGR data source with driver: GeoJSON 
Source: "/Users/jens/.cancensus_cache/CM_geo_6b42dc9bcf266cb7c9bfbbbf40889c49.geojson", layer: "OGRGeoJSON"
with 39 features
It has 11 fields
data_ct=get_census(dataset = 'CA16', regions=regions,vectors=vectors,geo_format='sp',labels='short',level='CT') %>% prep_data
OGR data source with driver: GeoJSON 
Source: "/Users/jens/.cancensus_cache/CM_geo_4a5626a6774fefc22c656038b44803a8.geojson", layer: "OGRGeoJSON"
with 478 features
It has 11 fields
data_da=get_census(dataset = 'CA16', regions=regions,vectors=vectors,geo_format='sp',labels='short',level='DA') %>% prep_data
OGR data source with driver: GeoJSON 
Source: "/Users/jens/.cancensus_cache/CM_geo_e0f185a0c335b032df17fa063ccc9238.geojson", layer: "OGRGeoJSON"
with 3450 features
It has 10 fields
data_db=get_census(dataset = 'CA16', regions=regions,geo_format='sp',labels='short',level='DB')
OGR data source with driver: GeoJSON 
Source: "/Users/jens/.cancensus_cache/CM_geo_ebd06436dd29d8db88d687516f5a8c04.geojson", layer: "OGRGeoJSON"
with 15197 features
It has 10 fields

which we then re-aggregate to make sure we don’t miss overall counts due to privacy cutoffs distribute them proportionally among the population.

data_ct@data <- dot_density.proportional_re_aggregate(data=data_ct@data,parent_data=data_csd@data,geo_match=setNames("GeoUID","CSD_UID"),categories=categories,base="Population")
data_da@data <- dot_density.proportional_re_aggregate(data=data_da@data,parent_data=data_ct@data,geo_match=setNames("GeoUID","CT_UID"),categories=categories,base="Population")
data_db@data <- dot_density.proportional_re_aggregate(data=data_db@data,parent_data=data_da@data,geo_match=setNames("GeoUID","DA_UID"),categories=categories,base="Population")

##Map All that’s left to do is to covert our re-aggregated block-level data to dots, using the dot_density.compute_dots function from the dotdensity package and feed it into the dot_density.dots_map function to add them to our basemap. As a scale we take one dot to represent 5 people.

scale=5

To better understand the effect of the dot_density.proportional_reaggregate function we show the dot-density maps using data from different aggregation levels.

CSD level

# 1 dot = 5 immigrants
dots <- dot_density.compute_dots(geo_data = data_csd, categories = categories, scale=scale) %>% st_as_sf
basemap +
  # zoom in a bit
  coord_sf(xlim=c(-123.29,-122.6), ylim=c(49.02,49.35)) +
  scale_colour_manual(values = colors) +
  labs(color = "",
                title="Immigrants 2011 - 2016",
                caption="Source: StatCan Census 2016 via cancensus & CensusMapper.ca",
                subtitle = paste0("1 dot = ",scale," people")) + 
  geom_sf(data=dots,aes(color=Category),alpha=0.75,size=0.25,show.legend = "point")
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

ggsave('../images/recent_immigrants_CSD.png',width=26,height=26)

CT Level Data

# 1 dot = 5 immigrants
dots <- dot_density.compute_dots(geo_data = data_ct, categories = categories, scale=scale) %>% st_as_sf
basemap +
  # zoom in a bit
  coord_sf(xlim=c(-123.29,-122.6), ylim=c(49.02,49.35)) +
  scale_colour_manual(values = colors) +
  labs(color = "",
                title="Immigrants 2011 - 2016",
                caption="Source: StatCan Census 2016 via cancensus & CensusMapper.ca",
                subtitle = paste0("1 dot = ",scale," people")) + 
  geom_sf(data=dots,aes(color=Category),alpha=0.75,size=0.25,show.legend = "point")
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

ggsave('../images/recent_immigrants_CT.png',width=26,height=26)

DA Level Data

# 1 dot = 5 immigrants
dots <- dot_density.compute_dots(geo_data = data_da, categories = categories, scale=scale) %>% st_as_sf
basemap +
  # zoom in a bit
  coord_sf(xlim=c(-123.29,-122.6), ylim=c(49.02,49.35)) +
  scale_colour_manual(values = colors) +
  labs(color = "",
                title="Immigrants 2011 - 2016",
                caption="Source: StatCan Census 2016 via cancensus & CensusMapper.ca",
                subtitle = paste0("1 dot = ",scale," people")) + 
  geom_sf(data=dots,aes(color=Category),alpha=0.75,size=0.25,show.legend = "point")
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

ggsave('../images/recent_immigrants_DA.png',width=26,height=26)

DB Level Data

# 1 dot = 5 immigrants
dots <- dot_density.compute_dots(geo_data = data_db, categories = categories, scale=scale) %>% st_as_sf
LS0tCnRpdGxlOiAiUmVjZW50IEltbWlncmFudHMiCmF1dGhvcjogIkplbnMgdm9uIEJlcmdtYW5uIgpkYXRlOiAiMjAxNy0wOC0yNiIKb3V0cHV0OiBodG1sX25vdGVib29rCnZpZ25ldHRlOiA+CiAgJVxWaWduZXR0ZUluZGV4RW50cnl7UmVjZW50IEltbWlncmFudHN9CiAgJVxWaWduZXR0ZUVuZ2luZXtrbml0cjo6cm1hcmtkb3dufQogICVcVmlnbmV0dGVFbmNvZGluZ3tVVEYtOH0KLS0tCgpNdWx0aS1jYXRlZ29yeSBkb3QtZGVuc2l0eSBtYXBzIG9mdGVuIHdvcmsgd2VsbCB3aGVuIHRoZSBjYXRlZ29yaWVzIGNsdXN0ZXIgZ2VvZ3JhcGhpY2FsbHkuIFJlY2VudCBpbW1pZ3JhbnRzIGJ5CmNvdW50cnkgb2Ygb3JpZ2luIHdvcmsgd2VsbCBmb3IgdGhpcy4KIyMgRGF0YQpGaXJzdCB3ZSBncmFiIHRoZSBpbW1pZ3JhbnQgZGF0YSB2aWEgW2NhbmNlbnN1c10oaHR0cHM6Ly9naXRodWIuY29tL21vdW50YWluTWF0aC9jYW5jZW5zdXMpLCBtYWtpbmcgdXNlIG9mIHRoZSBbQ2Vuc3VzTWFwcGVyIEFQSSB0b29sXShodHRwczovL2NlbnN1c21hcHBlci5jYS9hcGkvQ0ExMSkgdG8gc2VsZWN0IHRoZSByZWdpb25zIGFuZCB2YXJpYWJsZXMgd2UgbmVlZC4KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIm1vdW50YWlubWF0aC9jYW5jZW5zdXMiKQpsaWJyYXJ5KGNhbmNlbnN1cykKbGlicmFyeShkb3RkZW5zaXR5KQojIG9wdGlvbnMoY2FuY2Vuc3VzLmFwaV9rZXk9J3lvdXJfYXBpX2tleScpCnJlZ2lvbnM9bGlzdChDTUE9IjU5OTMzIikKdmVjdG9yc18yMDExPWMoInZfQ0ExMU5fMjY1Iiwidl9DQTExTl8yNjgiLCJ2X0NBMTFOXzMwNCIsInZfQ0ExMU5fMzM0Iiwidl9DQTExTl8zNzMiLCJ2X0NBMTFOXzM3NiIsInZfQ0ExMU5fMzc5Iiwidl9DQTExTl8zODIiKQp2ZWN0b3JzPWMoInZfQ0ExNl8zNjM2Iiwidl9DQTE2XzM2MzkiLCJ2X0NBMTZfMzY2OSIsInZfQ0ExNl8zNjk5Iiwidl9DQTE2XzM3NDEiLCJ2X0NBMTZfMzgxMCIsInZfQ0ExNl8zNzUwIiwidl9DQTE2XzM3NTYiLCJ2X0NBMTZfMzc4MyIpCmBgYAoKV2UgY2hvb3NlIHRoZSBjYXRlZ29yaWVzIGFuZCBjb2xvdXJzIHdlIHdhbnQgdG8gbWFwIGFuZCBkZWZpbmUgYSBjb252ZW5pZW5jZSBmdW5jdGlvbiB0byByZW5hbWUgdGhlIHZhcmlhYmxlcyBhbmQgY29tcHV0ZSB0aGUgcWFudGl0aWVzIGZvciB0aGUgb3RoZXIgYXNpYW4gY291bnRyaWVzIHRoYXQgd2UgZG9uJ3QgYnJlYWsgb3V0LgpgYGB7cn0KY2F0ZWdvcmllcz1jKCJBbWVyaWNhcyIsIkV1cm9wZSIsIkFmcmljYSArIE9jZWFuaWEiLCJQaGlsaXBwaW5lcyIsIkNoaW5hIiwiSW5kaWEiLCJPdGhlciBBc2lhbiBDb3VudHJpZXMiKQpjb2xvcnM9YygiIzdhMDE3NyIsICIjMzMzM2NjIiwgIiNmZjAwZmYiLCAiIzAwZmZmZiIsICIjZmYxYTFjIiwgIiM0ZGZmNGEiLCAiI2ZmZmYzMyIpCgpwcmVwX2RhdGEgPC0gZnVuY3Rpb24oZ2VvKXsKICBkYXRhIDwtIGdlbyAlPiUgcmVwbGFjZShpcy5uYSguKSwgMCkKICBkYXRhIDwtIHJlbmFtZShkYXRhLAogICAgdG90YWw9dl9DQTE2XzM2MzYsCiAgICBBbWVyaWNhcz12X0NBMTZfMzYzOSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICBFdXJvcGU9dl9DQTE2XzM2NjksCiAgICBQaGlsaXBwaW5lcz12X0NBMTZfMzc4MywKICAgIENoaW5hPXZfQ0ExNl8zNzUwLAogICAgSW5kaWE9dl9DQTE2XzM3NTYpCiAgZGF0YSAlPiUgbXV0YXRlKGBPdGhlciBBc2lhbiBDb3VudHJpZXNgID0gdl9DQTE2XzM3NDEtUGhpbGlwcGluZXMtQ2hpbmEtSW5kaWEsCiAgICAgICAgICAgICAgICAgICAgIGBBZnJpY2EgKyBPY2VhbmlhYD12X0NBMTZfMzY5OSArIHZfQ0ExNl8zODEwKQp9CnByZXBfZGF0YV8yMDExIDwtIGZ1bmN0aW9uKGdlbyl7CiAgZGF0YSA8LSBnZW8gJT4lIHJlcGxhY2UoaXMubmEoLiksIDApCiAgZGF0YSA8LSByZW5hbWUoZGF0YSwKICAgIHRvdGFsPXZfQ0ExMU5fMjY1LAogICAgQW1lcmljYXM9dl9DQTExTl8yNjgsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgRXVyb3BlPXZfQ0ExMU5fMzA0LAogICAgQWZyaWNhPXZfQ0ExMU5fMzM0LAogICAgUGhpbGlwcGluZXM9dl9DQTExTl8zNzYsCiAgICBDaGluYT12X0NBMTFOXzM3OSwKICAgIEluZGlhPXZfQ0ExMU5fMzgyKQogIGRhdGEgJT4lIG11dGF0ZShgT3RoZXIgQXNpYW4gQ291bnRyaWVzYCA9IHZfQ0ExMU5fMzczLVBoaWxpcHBpbmVzLUNoaW5hLUluZGlhKQp9CmBgYAoKCk5leHQgd2UgZ3JhYiB0aGUgZGF0YSB2aWEgYGNhbmNlbnN1c2AsCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmRhdGFfY3NkPWdldF9jZW5zdXMoZGF0YXNldCA9ICdDQTE2JywgcmVnaW9ucz1yZWdpb25zLHZlY3RvcnM9dmVjdG9ycyxnZW9fZm9ybWF0PSdzZicsbGFiZWxzPSdzaG9ydCcsbGV2ZWw9J0NTRCcpICU+JSBwcmVwX2RhdGEKZGF0YV9jdD1nZXRfY2Vuc3VzKGRhdGFzZXQgPSAnQ0ExNicsIHJlZ2lvbnM9cmVnaW9ucyx2ZWN0b3JzPXZlY3RvcnMsZ2VvX2Zvcm1hdD0nc2YnLGxhYmVscz0nc2hvcnQnLGxldmVsPSdDVCcpICU+JSBwcmVwX2RhdGEKZGF0YV9kYT1nZXRfY2Vuc3VzKGRhdGFzZXQgPSAnQ0ExNicsIHJlZ2lvbnM9cmVnaW9ucyx2ZWN0b3JzPXZlY3RvcnMsZ2VvX2Zvcm1hdD0nc2YnLGxhYmVscz0nc2hvcnQnLGxldmVsPSdEQScpICU+JSBwcmVwX2RhdGEKZGF0YV9kYj1nZXRfY2Vuc3VzKGRhdGFzZXQgPSAnQ0ExNicsIHJlZ2lvbnM9cmVnaW9ucyxnZW9fZm9ybWF0PSdzZicsbGFiZWxzPSdzaG9ydCcsbGV2ZWw9J0RCJykKYGBgCgp3aGljaCB3ZSB0aGVuIHJlLWFnZ3JlZ2F0ZSB0byBtYWtlIHN1cmUgd2UgZG9uJ3QgbWlzcyBvdmVyYWxsIGNvdW50cyBkdWUgdG8gcHJpdmFjeSBjdXRvZmZzIGRpc3RyaWJ1dGUgdGhlbQpwcm9wb3J0aW9uYWxseSBhbW9uZyB0aGUgcG9wdWxhdGlvbi4KYGBge3J9CiMgY3QgbGV2ZWwgZGF0YSBkb2VzIG5vdCBhbHdheXMgYWxpZ24gd2l0aCBDU0QsIHNvIHVzZSBjYXJlIHdoZW4gZG9pbmcgdGhpcwpkYXRhX2N0IDwtIGRvdF9kZW5zaXR5LnByb3BvcnRpb25hbF9yZV9hZ2dyZWdhdGUoZGF0YT1kYXRhX2N0LHBhcmVudF9kYXRhPWRhdGFfY3NkLGdlb19tYXRjaD1zZXROYW1lcygiR2VvVUlEIiwiQ1NEX1VJRCIpLGNhdGVnb3JpZXM9Y2F0ZWdvcmllcyxiYXNlPSJQb3B1bGF0aW9uIikKZGF0YV9kYSA8LSBkb3RfZGVuc2l0eS5wcm9wb3J0aW9uYWxfcmVfYWdncmVnYXRlKGRhdGE9ZGF0YV9kYSxwYXJlbnRfZGF0YT1kYXRhX2N0LGdlb19tYXRjaD1zZXROYW1lcygiR2VvVUlEIiwiQ1RfVUlEIiksY2F0ZWdvcmllcz1jYXRlZ29yaWVzLGJhc2U9IlBvcHVsYXRpb24iKQpkYXRhX2RiIDwtIGRvdF9kZW5zaXR5LnByb3BvcnRpb25hbF9yZV9hZ2dyZWdhdGUoZGF0YT1kYXRhX2RiLHBhcmVudF9kYXRhPWRhdGFfZGEsZ2VvX21hdGNoPXNldE5hbWVzKCJHZW9VSUQiLCJEQV9VSUQiKSxjYXRlZ29yaWVzPWNhdGVnb3JpZXMsYmFzZT0iUG9wdWxhdGlvbiIpCmBgYAoKCmBgYHtyLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCmJnX2NvbG9yPSIjMTExMTExIgpiYXNlX2NvbG9yPSIjMzMzMzMzIgp0ZXh0X2NvbG9yPSIjZWVlZWVlIgp0aGVtZV9vcHRzPC1saXN0KHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSBiZ19jb2xvciwgY29sb3VyID0gTkEpLAogICAgICAgICAgICAgICAgICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPWJnX2NvbG9yLCBzaXplPTEsbGluZXR5cGU9InNvbGlkIixjb2xvcj10ZXh0X2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT03MCxoanVzdCA9IDAuNSwgY29sb3I9dGV4dF9jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTUwLGhqdXN0ID0gMC41LCBjb2xvcj10ZXh0X2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZT0yNSwgY29sb3I9dGV4dF9jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MzUsIGNvbG9yPXRleHRfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MzUsIGNvbG9yPXRleHRfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9YmdfY29sb3IsIHNpemU9MSxsaW5ldHlwZT0ic29saWQiLGNvbG9yPWJnX2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQua2V5ID0gZWxlbWVudF9yZWN0KGZpbGwgPSBiZ19jb2xvcixjb2xvciA9IGJnX2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgzLCAnbGluZXMnKSwKICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikpCgpiYXNlbWFwIDwtICAgZ2dwbG90KGRhdGFfY3NkKSArCiAgICBnZW9tX3NmKGZpbGwgPSBiYXNlX2NvbG9yLCBzaXplPTAuMSwgY29sb3IgPSAnZ3JleScpICsKICAgIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQobnJvdz0xLG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0xNSkpKSArCiAgICAjY29vcmRfc2YocHJvamVjdGlvbj0ibGFtYmVydCIsIGxhdDA9NDksIGxhdD00OS40KSArCiAgICB0aGVtZV9vcHRzCgpgYGAKCgojI01hcApBbGwgdGhhdCdzIGxlZnQgdG8gZG8gaXMgdG8gY292ZXJ0IG91ciByZS1hZ2dyZWdhdGVkIGJsb2NrLWxldmVsIGRhdGEgdG8gZG90cywgdXNpbmcgdGhlIGBkb3RfZGVuc2l0eS5jb21wdXRlX2RvdHNgCmZ1bmN0aW9uIGZyb20gdGhlIFtgZG90ZGVuc2l0eWAgcGFja2FnZV0oKSBhbmQgZmVlZCBpdCBpbnRvIHRoZSBgZG90X2RlbnNpdHkuZG90c19tYXBgIGZ1bmN0aW9uIHRvIGFkZCB0aGVtIHRvCm91ciBiYXNlbWFwLiBBcyBhIHNjYWxlIHdlIHRha2Ugb25lIGRvdCB0byByZXByZXNlbnQgNSBwZW9wbGUuCmBgYHtyfQpzY2FsZT01CmBgYAoKClRvIGJldHRlciB1bmRlcnN0YW5kIHRoZSBlZmZlY3Qgb2YgdGhlICoqZG90X2RlbnNpdHkucHJvcG9ydGlvbmFsX3JlYWdncmVnYXRlKiogZnVuY3Rpb24gd2Ugc2hvdyB0aGUgZG90LWRlbnNpdHkgbWFwcyB1c2luZyBkYXRhIGZyb20gZGlmZmVyZW50IGFnZ3JlZ2F0aW9uIGxldmVscy4KCiMjIyBDU0QgbGV2ZWwKYGBge3IsIGZpZy5oZWlnaHQ9MTQsIGZpZy53aWR0aD0xNCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyAxIGRvdCA9IDUgaW1taWdyYW50cwpkb3RzIDwtIGRvdF9kZW5zaXR5LmNvbXB1dGVfZG90cyhnZW9fZGF0YSA9IGRhdGFfY3NkLCBjYXRlZ29yaWVzID0gY2F0ZWdvcmllcywgc2NhbGU9c2NhbGUpICU+JSBzdF9hc19zZgpiYXNlbWFwICsKICAjIHpvb20gaW4gYSBiaXQKICBjb29yZF9zZih4bGltPWMoLTEyMy4yOSwtMTIyLjYpLCB5bGltPWMoNDkuMDIsNDkuMzUpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMpICsKICBsYWJzKGNvbG9yID0gIiIsCiAgICAgICAgICAgICAgICB0aXRsZT0iSW1taWdyYW50cyAyMDExIC0gMjAxNiIsCiAgICAgICAgICAgICAgICBjYXB0aW9uPSJTb3VyY2U6IFN0YXRDYW4gQ2Vuc3VzIDIwMTYgdmlhIGNhbmNlbnN1cyAmIENlbnN1c01hcHBlci5jYSIsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMCgiMSBkb3QgPSAiLHNjYWxlLCIgcGVvcGxlIikpICsgCiAgZ2VvbV9zZihkYXRhPWRvdHMsYWVzKGNvbG9yPUNhdGVnb3J5KSxhbHBoYT0wLjc1LHNpemU9MC4yNSxzaG93LmxlZ2VuZCA9ICJwb2ludCIpCgpnZ3NhdmUoJy4uL2ltYWdlcy9yZWNlbnRfaW1taWdyYW50c19DU0QucG5nJyx3aWR0aD0yNixoZWlnaHQ9MjYpCmBgYAoKIyMjIENUIExldmVsIERhdGEKYGBge3IsIGZpZy5oZWlnaHQ9MTQsIGZpZy53aWR0aD0xNCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyAxIGRvdCA9IDUgaW1taWdyYW50cwpkb3RzIDwtIGRvdF9kZW5zaXR5LmNvbXB1dGVfZG90cyhnZW9fZGF0YSA9IGRhdGFfY3QsIGNhdGVnb3JpZXMgPSBjYXRlZ29yaWVzLCBzY2FsZT1zY2FsZSkgJT4lIHN0X2FzX3NmCmJhc2VtYXAgKwogICMgem9vbSBpbiBhIGJpdAogIGNvb3JkX3NmKHhsaW09YygtMTIzLjI5LC0xMjIuNiksIHlsaW09Yyg0OS4wMiw0OS4zNSkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGNvbG9ycykgKwogIGxhYnMoY29sb3IgPSAiIiwKICAgICAgICAgICAgICAgIHRpdGxlPSJJbW1pZ3JhbnRzIDIwMTEgLSAyMDE2IiwKICAgICAgICAgICAgICAgIGNhcHRpb249IlNvdXJjZTogU3RhdENhbiBDZW5zdXMgMjAxNiB2aWEgY2FuY2Vuc3VzICYgQ2Vuc3VzTWFwcGVyLmNhIiwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKCIxIGRvdCA9ICIsc2NhbGUsIiBwZW9wbGUiKSkgKyAKICBnZW9tX3NmKGRhdGE9ZG90cyxhZXMoY29sb3I9Q2F0ZWdvcnkpLGFscGhhPTAuNzUsc2l6ZT0wLjI1LHNob3cubGVnZW5kID0gInBvaW50IikKCmdnc2F2ZSgnLi4vaW1hZ2VzL3JlY2VudF9pbW1pZ3JhbnRzX0NULnBuZycsd2lkdGg9MjYsaGVpZ2h0PTI2KQpgYGAKCgoKIyMjIERBIExldmVsIERhdGEKYGBge3IsIGZpZy5oZWlnaHQ9MTQsIGZpZy53aWR0aD0xNCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyAxIGRvdCA9IDUgaW1taWdyYW50cwpkb3RzIDwtIGRvdF9kZW5zaXR5LmNvbXB1dGVfZG90cyhnZW9fZGF0YSA9IGRhdGFfZGEsIGNhdGVnb3JpZXMgPSBjYXRlZ29yaWVzLCBzY2FsZT1zY2FsZSkgJT4lIHN0X2FzX3NmCmJhc2VtYXAgKwogICMgem9vbSBpbiBhIGJpdAogIGNvb3JkX3NmKHhsaW09YygtMTIzLjI5LC0xMjIuNiksIHlsaW09Yyg0OS4wMiw0OS4zNSkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGNvbG9ycykgKwogIGxhYnMoY29sb3IgPSAiIiwKICAgICAgICAgICAgICAgIHRpdGxlPSJJbW1pZ3JhbnRzIDIwMTEgLSAyMDE2IiwKICAgICAgICAgICAgICAgIGNhcHRpb249IlNvdXJjZTogU3RhdENhbiBDZW5zdXMgMjAxNiB2aWEgY2FuY2Vuc3VzICYgQ2Vuc3VzTWFwcGVyLmNhIiwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKCIxIGRvdCA9ICIsc2NhbGUsIiBwZW9wbGUiKSkgKyAKICBnZW9tX3NmKGRhdGE9ZG90cyxhZXMoY29sb3I9Q2F0ZWdvcnkpLGFscGhhPTAuNzUsc2l6ZT0wLjI1LHNob3cubGVnZW5kID0gInBvaW50IikKCmdnc2F2ZSgnLi4vaW1hZ2VzL3JlY2VudF9pbW1pZ3JhbnRzX0RBLnBuZycsd2lkdGg9MjYsaGVpZ2h0PTI2KQpgYGAKCgojIyMgREIgTGV2ZWwgRGF0YQpgYGB7ciwgZmlnLmhlaWdodD0xNCwgZmlnLndpZHRoPTE0LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIDEgZG90ID0gNSBpbW1pZ3JhbnRzCmRvdHMgPC0gZG90X2RlbnNpdHkuY29tcHV0ZV9kb3RzKGdlb19kYXRhID0gZGF0YV9kYiwgY2F0ZWdvcmllcyA9IGNhdGVnb3JpZXMsIHNjYWxlPXNjYWxlKSAlPiUgc3RfYXNfc2YKYmFzZW1hcCArCiAgIyB6b29tIGluIGEgYml0CiAgY29vcmRfc2YoeGxpbT1jKC0xMjMuMjksLTEyMi42KSwgeWxpbT1jKDQ5LjAyLDQ5LjM1KSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gY29sb3JzKSArCiAgbGFicyhjb2xvciA9ICIiLAogICAgICAgICAgICAgICAgdGl0bGU9IkltbWlncmFudHMgMjAxMSAtIDIwMTYiLAogICAgICAgICAgICAgICAgY2FwdGlvbj0iU291cmNlOiBTdGF0Q2FuIENlbnN1cyAyMDE2IHZpYSBjYW5jZW5zdXMgJiBDZW5zdXNNYXBwZXIuY2EiLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAoIjEgZG90ID0gIixzY2FsZSwiIHBlb3BsZSIpKSArIAogIGdlb21fc2YoZGF0YT1kb3RzLGFlcyhjb2xvcj1DYXRlZ29yeSksYWxwaGE9MC43NSxzaXplPTAuMjUsc2hvdy5sZWdlbmQgPSAicG9pbnQiKQoKZ2dzYXZlKCcuLi9pbWFnZXMvcmVjZW50X2ltbWlncmFudHNfREIucG5nJyx3aWR0aD0yNixoZWlnaHQ9MjYpCmBgYAoKCg==